/**
 * @file JSONObject.cpp
 */
#include "JSONNode.h"
#include "../common/UtilFuncT.h"
#include "../common/CppMemPool.h"
#include "../lang/Primitive.h"
#include "JSONException.h"

using namespace cppflib::collections;
using namespace cppflib::collections::tree;
using namespace cppflib::lang;

namespace cppflib
{

namespace json
{

JSONObject::JSONObject(void)
{
   pNVPairList = new LinkedHashtableT<CFString, BaseObject>();
   cppmempool::Retain(pNVPairList);
}

JSONObject::~JSONObject(void)
{
   cppmempool::Release(pNVPairList);
}

JSONNode::nodetype_t JSONObject::GetNodeType()
{
   return JSONNode::JN_OBJECT;
}

JSONObject * JSONObject::CastToJSONObject()
{
   return this;
}

JSONArray * JSONObject::CastToJSONArray()
{
   throw JSONException(_S("This is not a JSONArray"));
}

/**
 *  Get the value type of a name-value pair at the specified index
 */
JSONNode::valuetype_t JSONObject::GetValueType(u32_t index)
{
   return JSONNode::GetValueType(this->Get(index));
}

/**
 *  Get the value type of a name-value pair by the specified name
 */
JSONNode::valuetype_t JSONObject::GetValueType(const CFString& name)
{
   return JSONNode::GetValueType(this->Get(name));
}

sz_t JSONObject::GetNumberOfNameValuePairs()
{
   return pNVPairList->GetCount();
}

/**
 *  Get value by index
 */
BaseObject * JSONObject::Get(u32_t index)
{
   if (index >= pNVPairList->GetCount())
      throw JSONException(_S("Index is greater than or equal to number of name-value pairs"));

   const CFString *pKey = NULL;
   BaseObject *pVal = NULL;
   pNVPairList->Get(static_cast<int>(index), pKey, pVal);
   return pVal;
}

/**
 *  Get value by name. Throw exception if name does not exist.
 */
BaseObject * JSONObject::Get(const CFString& name)
{
   BaseObject *pVal = pNVPairList->Get(name);
   if (pVal == NULL) {
      if (!pNVPairList->ContainsKey(name))
         throw JSONException(_S("Name does not exist"));
   }
   return pVal;
}

CFString * JSONObject::GetString(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<CFString>(pVal))
      throw JSONException(_S("Value is not a string"));
   return static_cast<CFString*>(pVal);
}

bool JSONObject::GetBoolean(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<Boolean>(pVal))
      throw JSONException(_S("Value is not a boolean"));
   return static_cast<Boolean*>(pVal)->Value();
}

double JSONObject::GetDouble(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<Double>(pVal))
      throw JSONException(_S("Value is not a double"));
   return static_cast<Double*>(pVal)->Value();
}

int JSONObject::GetInt32(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<Int32>(pVal))
      throw JSONException(_S("Value is not a 32 bit integer"));
   return static_cast<Int32*>(pVal)->Value();
}

i64_t JSONObject::GetInt64(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (cppflib::InstanceOf<Int64>(pVal)) 
      return static_cast<Int64*>(pVal)->Value();
   else if (cppflib::InstanceOf<Int32>(pVal))
      return static_cast<Int32*>(pVal)->Value();
   
   throw JSONException(_S("Value is not an integer"));
}

JSONObject * JSONObject::GetJSONObject(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<JSONObject>(pVal))
      throw JSONException(_S("Value is not a JSONObject"));
   return static_cast<JSONObject*>(pVal);
}

JSONArray * JSONObject::GetJSONArray(const CFString& name)
{
   BaseObject *pVal = Get(name);
   if (!cppflib::InstanceOf<JSONArray>(pVal))
      throw JSONException(_S("Value is not a JSONArray"));
   return static_cast<JSONArray*>(pVal);
}

bool JSONObject::Contains(const CFString& name)
{
   return pNVPairList->ContainsKey(name);
}

bool JSONObject::IsNull(const CFString& name)
{
   return (GetValueType(name) == JSONNode::JVL_NULL);
}

PRIVATE void __ThrowIfEmptyName(CFString *pName)
{
   if (CFString::IsNullOrEmpty(pName)) throw JSONException(_S("Name cannot be empty"));
}

PRIVATE void __ThrowIfEmptyNameAndVal(CFString *pName, BaseObject *pVal)
{
   if (CFString::IsNullOrEmpty(pName)) throw JSONException(_S("Name cannot be empty"));
   if (pVal == NULL) throw JSONException(_S("Value cannot be NULL"));
}

void JSONObject::Set(CFString *pName, CFString *pVal)
{
   __ThrowIfEmptyNameAndVal(pName, pVal);
   pNVPairList->Set(pName, pVal);
}

void JSONObject::Set(CFString *pName, bool val)
{
   __ThrowIfEmptyName(pName);
   pNVPairList->Set(pName, new Boolean(val));
}

void JSONObject::Set(CFString *pName, double val)
{
   __ThrowIfEmptyName(pName);
   pNVPairList->Set(pName, new Double(val));
}

void JSONObject::Set(CFString *pName, int val)
{
   __ThrowIfEmptyName(pName);
   pNVPairList->Set(pName, new Int32(val));
}

void JSONObject::Set(CFString *pName, i64_t val)
{
   __ThrowIfEmptyName(pName);
   pNVPairList->Set(pName, new Int64(val));
}

/**
 *  Set a JSONObject and will automatically set it as a child of this JSONObject.
 */
void JSONObject::Set(CFString *pName, JSONObject *pJObj)
{
   __ThrowIfEmptyNameAndVal(pName, pJObj);
   pNVPairList->Set(pName, pJObj);
   this->AddChild(pJObj);
}

/**
 *  Set a JSONArray and will automatically set it as a child of this JSONObject.
 */
void JSONObject::Set(CFString *pName, JSONArray *pJArray)
{
   __ThrowIfEmptyNameAndVal(pName, pJArray);
   pNVPairList->Set(pName, pJArray);
   this->AddChild(pJArray);
}

void JSONObject::SetNull(CFString *pName)
{
   __ThrowIfEmptyName(pName);
   pNVPairList->Set(pName, NULL);
}

/**
 *  Remove a name-value pair by name
 */
bool JSONObject::Remove(const CFString& name)
{
   return pNVPairList->Remove(name);
}

/**
 *  Remove a name-value pair by index
 */
bool JSONObject::Remove(u32_t index)
{
   if (index < pNVPairList->GetCount()) {
      pNVPairList->RemoveAt(static_cast<int>(index));
      return true;
   }

   return false;
}

} // end of namespace json

} // end of namespace cppflib
